package org.millioncall.yueyoga.common.utils;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.servlet.ServletRequest;
import javax.servlet.http.HttpServletRequest;

import org.millioncall.openplatform.util.WebAppUtils;

import com.oreilly.servlet.multipart.FilePart;
import com.oreilly.servlet.multipart.FileRenamePolicy;
import com.oreilly.servlet.multipart.MultipartParser;
import com.oreilly.servlet.multipart.ParamPart;
import com.oreilly.servlet.multipart.Part;

//Last modified by zangjy on 2012-3-31
//Last modified by zangjy on 2012-5-5
public class CosMultipartRequest {
	private static final int DEFAULT_MAX_POST_SIZE = 1024 * 1024;// 默认大小1m
	protected Hashtable<String, List<String>> parameters = new Hashtable<String, List<String>>();// 表单参数列表
	protected Map<String, UploadedFile> files = new HashMap<String, UploadedFile>();// 所有已上传的文件

	public CosMultipartRequest(HttpServletRequest request, String saveDirectory)
			throws IOException {
		this(request, saveDirectory, DEFAULT_MAX_POST_SIZE);
	}

	public CosMultipartRequest(HttpServletRequest request,
			String saveDirectory, int maxPostSize) throws IOException {
		this(request, saveDirectory, maxPostSize, null, null);
	}

	public CosMultipartRequest(HttpServletRequest request,
			String saveDirectory, String encoding) throws IOException {
		this(request, saveDirectory, DEFAULT_MAX_POST_SIZE, encoding, null);
	}

	public CosMultipartRequest(HttpServletRequest request,
			String saveDirectory, int maxPostSize, FileRenamePolicy policy)
			throws IOException {
		this(request, saveDirectory, maxPostSize, null, policy);
	}

	public CosMultipartRequest(HttpServletRequest request,
			String saveDirectory, int maxPostSize, String encoding)
			throws IOException {
		this(request, saveDirectory, maxPostSize, encoding, null);
	}

	public CosMultipartRequest(HttpServletRequest request,
			String saveDirectory, int maxPostSize, String encoding,
			FileRenamePolicy policy) throws IOException {
		if (request == null)
			throw new IllegalArgumentException("request cannot be null");
		if (saveDirectory == null)
			throw new IllegalArgumentException("saveDirectory cannot be null");
		if (maxPostSize <= 0) {
			throw new IllegalArgumentException("maxPostSize must be positive");
		}
		File dir = new File(saveDirectory);// 文件保存位置，临时上传目录

		if (!dir.isDirectory()) {
			throw new IllegalArgumentException("Not a directory: "
					+ saveDirectory);
		}

		if (!dir.canWrite()) {
			throw new IllegalArgumentException("Not writable: " + saveDirectory);
		}

		// 创建一个上传表单解析器
		MultipartParser parser = new MultipartParser(request, maxPostSize,
				true, true, encoding);

		if (request.getQueryString() != null) {
			Map<String, String[]> queryParameters = WebAppUtils
					.parseQueryString(request.getQueryString());
			for (Entry<String, String[]> entry : queryParameters.entrySet()) {
				parameters.put(entry.getKey(), Arrays.asList(entry.getValue()));// 将数组格式的表单参数转为List格式
			}
		}

		Part part;
		while ((part = parser.readNextPart()) != null) {// 遍历每一个表单元素
			String name = part.getName();// 表单名称，即表单的name属性
			if (name == null) {
				throw new IOException(
						"Malformed input: parameter name missing (known Opera 7 bug)");
			}
			if (part.isParam()) {// 如果为普通参数表单元素，添加表单值到值列表中
				ParamPart paramPart = (ParamPart) part;
				String value = paramPart.getStringValue();
				List<String> existingValues = parameters.get(name);
				if (existingValues == null) {
					existingValues = new ArrayList<String>();
					parameters.put(name, existingValues);
				}
				existingValues.add(value);
			} else if (part.isFile()) {// 如果是文件表单元素
				FilePart filePart = (FilePart) part;
				String fileName = filePart.getFileName();
				if (fileName != null) {
					filePart.setRenamePolicy(policy);
					filePart.writeTo(dir);// 写入临时文件的过程
					files.put(
							name,
							new UploadedFile(dir.toString(), filePart
									.getFileName(), fileName, filePart
									.getContentType()));
				} else {
					files.put(name, new UploadedFile(null, null, null, null));
				}
			}
		}
	}

	public CosMultipartRequest(ServletRequest request, String saveDirectory)
			throws IOException {
		this((HttpServletRequest) request, saveDirectory);
	}

	public CosMultipartRequest(ServletRequest request, String saveDirectory,
			int maxPostSize) throws IOException {
		this((HttpServletRequest) request, saveDirectory, maxPostSize);
	}

	public Enumeration<String> getParameterNames() {
		return parameters.keys();
	}

	public Set<String> getFileNames() {
		return files.keySet();
	}

	public String getParameter(String name) {
		try {
			List<String> values = parameters.get(name);
			if (values == null || values.size() == 0) {
				return null;
			}
			return values.get(values.size() - 1);
		} catch (Exception e) {
			return null;
		}
	}

	public String[] getParameterValues(String name) {
		List<String> values = parameters.get(name);
		if (values == null || values.size() == 0) {
			return null;
		} else {
			return values.toArray(new String[values.size()]);
		}
	}

	public String getFilesystemName(String name) {
		try {
			UploadedFile file = files.get(name);
			return file.getFilesystemName(); // may be null
		} catch (Exception e) {
			return null;
		}
	}

	public String getOriginalFileName(String name) {
		try {
			UploadedFile file = (UploadedFile) files.get(name);
			return file.getOriginalFileName(); // may be null
		} catch (Exception e) {
			return null;
		}
	}

	public String getContentType(String name) {
		try {
			UploadedFile file = (UploadedFile) files.get(name);
			return file.getContentType(); // may be null
		} catch (Exception e) {
			return null;
		}
	}

	// 取得已上传的文件
	public File getFile(String name) {
		try {
			UploadedFile file = (UploadedFile) files.get(name);
			return file.getFile(); // may be null
		} catch (Exception e) {
			return null;
		}
	}
}

class UploadedFile {

	private String dir;
	private String filename;
	private String original;
	private String type;

	UploadedFile(String dir, String filename, String original, String type) {
		this.dir = dir;
		this.filename = filename;
		this.original = original;
		this.type = type;
	}

	public String getContentType() {
		return type;
	}

	public String getFilesystemName() {
		return filename;
	}

	public String getOriginalFileName() {
		return original;
	}

	public File getFile() {
		if (dir == null || filename == null) {
			return null;
		} else {
			return new File(dir + File.separator + filename);
		}
	}
}